#ifndef TRANSMISSION_H
#define TRANSMISSION_H
#include <iostream>
#include "utility.hpp"
#include <cstring>
//using namespace std;

extern bool is_big_endian;
bool IsBigEndian();

union Data{
    double d;
    char c[sizeof (double)];
    Data(const double data):d(data){}
    Data(){}
};
class test
{
public:
    test()
    {
        is_big_endian=IsBigEndian();
    }
    //char bytes[sizeof (double)]={0};
    void Double2ByteArray(void *dst,const double d)
    {
        data.d=d;
        char *arr=static_cast<char*>(dst);
        if(is_big_endian)
        {
            for (size_t i=0;i<sizeof (double);i++)
            {
                arr[i]=data.c[sizeof (data)-1];
            }
        }
        else
        {
            memcpy(dst,data.c,sizeof (double));
        }
    }
    void ByteArray2Double(double &d, const void *src)
    {
        const char *arr=static_cast<const char*>(src);
        if(is_big_endian)
        {
            for (size_t i=0;i<sizeof (double);i++)
            {
                data.c[sizeof (data)-1]=arr[i];
            }
        }
        else
        {
            memcpy(data.c,src,sizeof (double));
        }
        d=data.d;
    }
private:
    static bool is_big_endian;
    bool IsBigEndian()
    {
        union NUM
        {
            int a;
            char b;
        }num;
        num.a = 0x1234;
        if( num.b == 0x12 )
        {
            return true;
        }
        return false;
    }
    Data data;
};

const int data_len=8;
union BufferDoubleArray{
    double data[data_len];
    unsigned char data_byte[data_len*sizeof (double)];
};
union BufferIntArray{
    int data[data_len];
    unsigned char data_byte[data_len*sizeof (int)];
};
union BufferInt{
    int data;
    char data_byte[sizeof (int)];
};
union BufferDouble{
    int data;
    char data_byte[sizeof (double)];
};

union BufferDouble100
{
    double data_double[100]={0};
    char data_byte[100*sizeof (double)];
    BufferDouble100()
    {
        memset(data_double,0,sizeof (double)*100);
    }
    BufferDouble100(const double *array)
    {
        memcpy(data_double,array,sizeof (double)*100);
    }
    BufferDouble100(const char *array)
    {
        memcpy(data_byte,array,sizeof (char)*100*sizeof (double));
    }
    bool InputDoubleData(const double *src,const size_t len)
    {
        if(len>100)
            return false;
        memcpy(data_double,src,len);
        return true;
    }
    bool InputByteData(const char *src,const size_t len)
    {
        if(len>100*sizeof (double))
            return false;
        memcpy(data_byte,src,len);
        return true;
    }

    bool OutputDoubleData(double *dst,const size_t len)
    {
        if(len>100)
            return false;
        memcpy(dst,data_double,len);
        return true;
    }
    bool OutputByteData(char *dst,const size_t len)
    {
        if(len>100*sizeof (double))
            return false;
        memcpy(dst,data_byte,len);
        return true;
    }
};


union SocketBuffer
{
    double data_double[100]={0};
    char data_byte[100*sizeof (double)];
    SocketBuffer()
    {
        is_big_endian=IsBigEndian();

        //begin & version
        data_byte[0]=0x42;
        data_byte[1]=0x43;
        data_byte[2]=0x03;

        //len
        data_byte[3]=0x0;
        data_byte[4]=0x0;

        //id
        data_byte[5]=0x0;
        data_byte[6]=0x0;

        //data & crc
        memset(data_byte+7,0,sizeof (double)*100-7);
    }

    bool DataArray2SocketBuffer(u_char *out_data, int &out_len, const double *in_data,const size_t data_len, const int id=0)
    {
        if(!InputDoubleData(in_data,data_len))
            return false;
        out_len=9+sizeof (double)*data_len;

        if(is_big_endian)
        {
            //len
            data_byte[3]=out_len/0xff;
            data_byte[4]=out_len%0xff;
            //id
            data_byte[5]=id/0xff;
            data_byte[6]=id%0xff;
            //crc
            int crc= CalCRC16(data_byte, out_len-2);
            data_byte[out_len-2]=crc/0xff;
            data_byte[out_len-1]=crc%0xff;
        }
        else
        {
            //len
            data_byte[3]=out_len%0xff;
            data_byte[4]=out_len/0xff;
            //id
            data_byte[5]=id%0xff;
            data_byte[6]=id/0xff;
            //crc

            int crc= CalCRC16(data_byte, out_len-2);
            data_byte[out_len-2]=crc%0xff;
            data_byte[out_len-1]=crc/0xff;
        }


        out_data=data_byte;
        return true;
    }

    bool ParseSocketBuffer(int &id, int &double_data_num, double *double_data,char *out_data, int &out_len, const char *in_data,const size_t data_len)
    {

        if((data_len-4)%8!=0)
            return false;
        double_data_num=static_cast<int>((data_len-4)/8);

        if(is_big_endian)
        {
            id=in_data[0]*0x0100+in_data[1];
        }
        else
        {
            id=in_data[0]+in_data[1]*0x0100;
        }


        BufferDouble buffer[double_data_num];
        memcpy(buffer,in_data+2,data_len-4);
        //memcpy()


        if(!InputByteData(in_data,data_len))
            return false;
        out_len=9+sizeof (double)*data_len;

        if(is_big_endian)
        {
            //len
            data_byte[3]=out_len/0xff;
            data_byte[4]=out_len%0xff;
            //id
            data_byte[5]=id/0xff;
            data_byte[6]=id%0xff;
            //crc
            int crc= CalCRC16(data_byte, out_len-2);
            data_byte[out_len-2]=crc/0xff;
            data_byte[out_len-1]=crc%0xff;
        }
        else
        {
            //len
            data_byte[3]=out_len%0xff;
            data_byte[4]=out_len/0xff;
            //id
            data_byte[5]=id%0xff;
            data_byte[6]=id/0xff;
            //crc

            int crc= CalCRC16(data_byte, out_len-2);
            data_byte[out_len-2]=crc%0xff;
            data_byte[out_len-1]=crc/0xff;
        }


        out_data=data_byte;
        return true;
    }


    bool InputDoubleData(const double *src,const size_t len)
    {
        if(len>100-int(9/sizeof (double)+1))//extra data occupies 9 bytes, user can only send 100-int(9/sizeof (double)+1) double value
            return false;
        memcpy(data_byte+7,src, sizeof(double)*len);
        return true;
    }
    bool InputByteData(const char *src,const size_t len)
    {
        if(len>100*sizeof (double)-9)
            return false;
        memcpy(data_byte+7,src,len);
        return true;
    }

    bool OutputDoubleData(double *dst,const size_t len)
    {
        if(len>100-int(9/sizeof (double)+1))//extra data occupies 9 bytes, user can only send 100-int(9/sizeof (double)+1) double value
            return false;
        memcpy(dst,data_byte,sizeof (double)*len);
        return true;
    }
    bool OutputByteData(char *dst,const size_t len)
    {
        if(len>100*sizeof (double)-9)
            return false;
        memcpy(dst,data_byte,len);
        return true;
    }
};
#endif // TRANSMISSION_H
